外观
问题描述
无法从 Graphite 数据库导出 Sitespeed.io 性能监控数据到 CSV 格式进行分析和归档。
影响范围
所有需要导出 Graphite 中存储的 Sitespeed.io 性能指标数据的场景,包括:
- 性能数据分析和报告
- 历史数据归档
- 数据迁移和备份
前提条件
- Docker 环境正常运行
- Graphite Docker 容器 (docker-graphite-1) 已启动
- Sitespeed.io 数据已存储在 Graphite 中
- 具有 Docker 容器访问权限
解决方案
1. 准备数据导出脚本
创建 export-sitespeed-csv.sh 脚本文件,内容如下:
bash
#!/usr/bin/env bash
# export-sitespeed-csv.sh - Graphite Sitespeed 数据导出脚本
############## 0. 用户配置区(留空则使用默认值或交互)##############
# 1) 要导出的指标
METRICS=(mainDocumentTimings/dns timings/ttfb pageTimings/domContentLoadedTime timings/paintTiming/first-contentful-paint largestContentfulPaint/loadTime timings/fullyLoaded )
# 2) 要导出的 wsp 类型
WSP_TYPES=(mean.wsp)
# 3) 指定网址(目录名);留空数组 = 导出全部
SITE_URLS=()
# 4) 时间范围(自然语言或秒级时间戳)
START_TIME="" # 例:30 days ago
END_TIME="" # 例:now
############## 5. 固定路径 ##############
# 这里就是whisper的存储路径
GRAPHITE_STORAGE="/opt/graphite/storage/whisper"
# 这里就是whisper的脚本路径
WHISPER_FETCH="/opt/graphite/bin/whisper-fetch.py"
# 这里就是输出的目录
OUTPUT_DIR="/opt/graphite/storage/whisper/sitespeed_csv/$(date +"%Y-%m-%d_%H-%M-%S")"
mkdir -p "$OUTPUT_DIR"
############## 6. 时间处理函数 ##############
parse_time(){
local t="$1"
if [[ $t =~ ^[0-9]+$ ]]; then echo "$t"; else date -d "$t" +%s; fi
}
############## 7. 交互输入(若留空)##############
[[ -z "$START_TIME" ]] && read -rp "开始时间 (如 '2025-12-01 00:00:00' 或秒级时间戳): " START_TIME
[[ -z "$END_TIME" ]] && read -rp "结束时间 (如 '2025-12-28 00:00:00' 或秒级时间戳): " END_TIME
START_EPOCH=$(parse_time "$START_TIME")
END_EPOCH=$(parse_time "$END_TIME")
############## 8. 决定网址列表 ##############
PAGE_SUMMARY_DIR="$GRAPHITE_STORAGE/sitespeed_io/default/myTest1/pageSummary"
if [[ ${#SITE_URLS[@]} -eq 0 ]]; then
# 未指定 → 扫描全部子目录
mapfile -t SITE_URLS < <(find "$PAGE_SUMMARY_DIR" -mindepth 1 -maxdepth 1 -type d -exec basename {} \; | sort)
site_display="全部"
else
site_display="${SITE_URLS[*]}"
fi
[[ ${#SITE_URLS[@]} -eq 0 ]] && { echo "没有有效网址可导出,退出"; exit 0; }
############## 9. 回显导出信息 ##############
echo "开始导出 Sitespeed 指标数据..."
echo "存储路径: $GRAPHITE_STORAGE"
echo "输出目录: $OUTPUT_DIR"
echo "导出区间: $(date -d "@$START_EPOCH" '+%F %T') -> $(date -d "@$END_EPOCH" '+%F %T')"
echo "导出网址: $site_display"
echo "指标列表: ${METRICS[*]}"
echo "文件类型: ${WSP_TYPES[*]}"
echo "----------------------------------------"
############## 10. 主循环:每个指标一份 CSV ##############
for metric in "${METRICS[@]}"; do
# 将指标名称中的斜杠替换为下划线,作为文件名
metric_file_name=$(echo "$metric" | sed 's/\//_/g' | awk -F'_' '{print $NF}')
csv_file="$OUTPUT_DIR/${metric_file_name}.csv"
> "$csv_file"
echo "siteurl,timestamp,value" > "$csv_file"
echo "处理指标: $metric_file_name"
for wtype in "${WSP_TYPES[@]}"; do
# 遍历所有网址
for site in "${SITE_URLS[@]}"; do
site_dir="$PAGE_SUMMARY_DIR/$site"
[[ -d $site_dir ]] || { echo " 警告:网址目录不存在 [$site],已跳过"; continue; }
# 使用find命令查找该网址目录下指定指标和类型的wsp文件
# 支持大小写不敏感的匹配
wsp_files=$(find "$site_dir" -type f -name "*$wtype" | grep -i "$metric" || true)
if [ -z "$wsp_files" ]; then
continue
fi
# 处理每个找到的wsp文件
while IFS= read -r wsp_file; do
if [ ! -f "$wsp_file" ]; then
continue
fi
# 使用whisper-fetch.py导出数据,并转换为CSV格式
if ! $WHISPER_FETCH \
--from="$START_EPOCH" --until="$END_EPOCH" "$wsp_file" 2>/dev/null | grep -v None | \
awk -v site="$site" '
NF==2 && $2!="" {
# 时间戳转可读格式
cmd = "date -d @" $1 " +%Y-%m-%d\" \"%H:%M:%S"
cmd | getline datetime
close(cmd)
printf "%s,%s,%.15g\n", site, datetime, $2
}' >> "$csv_file"; then
echo " 导出失败: $site - $wsp_file"
continue
fi
echo " 成功导出数据: $site"
done <<< "$wsp_files"
done
done
# 检查是否有数据写入
data_lines=$(tail -n +2 "$csv_file" | wc -l)
if [ "$data_lines" -gt 0 ]; then
echo "指标 [$metric] 导出完成 -> $csv_file ($data_lines 行数据)"
else
echo "指标 [$metric] 未找到任何数据,删除空文件"
rm -f "$csv_file"
fi
echo ""
done
################### 11. 统计信息 ###################
total_files=0
total_lines=0
for csv in "$OUTPUT_DIR"/*.csv; do
((total_files++))
lines=$(tail -n +2 "$csv" 2>/dev/null | wc -l)
((total_lines += lines))
done
echo "统计信息:"
echo " 生成的CSV文件数: $total_files"
echo " 总数据行数: $total_lines"
echo " 输出目录: $OUTPUT_DIR"1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
2. 配置脚本权限
为脚本文件添加执行权限:
bash
chmod +x export-sitespeed-csv.sh1
3. 部署脚本到容器
将脚本文件拷贝到 Graphite Docker 容器中:
bash
docker cp ./export-sitespeed-csv.sh docker-graphite-1:/opt/graphite/bin/export-sitespeed-csv.sh1
确保容器名称 `docker-graphite-1` 与实际运行的容器名称一致,可以通过 `docker ps` 命令确认。
4. 执行数据导出
在容器中运行导出脚本:
bash
docker exec -it docker-graphite-1 bash /opt/graphite/bin/export-sitespeed-csv.sh1
脚本运行时会进行交互式输入,如果配置区中未设置时间范围,会提示用户输入开始时间和结束时间。
bash
[root@localhost sitespeed.io]# docker exec -it docker-graphite-1 bash /opt/graphite/bin/export-sitespeed-csv.sh
开始时间 (如 '2025-12-01 00:00:00' 或秒级时间戳): 2025-12-1
结束时间 (如 '2025-12-28 00:00:00' 或秒级时间戳): 2025-12-31
开始导出 Sitespeed 指标数据...
存储路径: /opt/graphite/storage/whisper
输出目录: /opt/graphite/storage/whisper/sitespeed_csv/2025-12-31_08-20-46
导出区间: 2025-12-01 00:00:00 -> 2025-12-31 00:00:00
导出网址: 全部
指标列表: mainDocumentTimings/dns timings/ttfb pageTimings/domContentLoadedTime timings/paintTiming/first-contentful-paint largestContentfulPaint/loadTime timings/fullyLoaded
文件类型: mean.wsp
----------------------------------------
/opt/graphite/bin/export-sitespeed-csv.sh: line 65: /opt/graphite/storage/whisper/sitespeed_csv/2025-12-31_08-20-46/dns.csv: No such file or directory
处理指标: dns
成功导出数据: about_proquest_com
成功导出数据: academic_eb_cnpereading_com
成功导出数据: academic_oup_com
成功导出数据: academic_oup_com
成功导出数据: access_clarivate_com
成功导出数据: agupubs_onlinelibrary_wiley_com
成功导出数据: alexanderstreet_com
成功导出数据: bestpractice_bmj_com
成功导出数据: bioone_org
成功导出数据: brillpublications_cn
成功导出数据: compass_astm_org
成功导出数据: database_worldlibrary_org
成功导出数据: dlut_cn_libguides_com
成功导出数据: esi_clarivate_com
成功导出数据: firstsearch_oclc_org
成功导出数据: firstsearch_oclc_org
成功导出数据: ieeexplore_ieee_org
成功导出数据: incites_clarivate_com
....
统计信息:
生成的CSV文件数: 6
总数据行数: 78357
输出目录: /opt/graphite/storage/whisper/sitespeed_csv/2025-12-31_08-20-46
[root@localhost sitespeed.io]# rsync -av --ignore-existing /docker/docker_data/volumes/docker_whisper/_data/sitespeed_csv/ \
/docker/sitespeed.io/sitespeed-result/sitespeed_csv/
sending incremental file list
created directory /docker/sitespeed.io/sitespeed-result/sitespeed_csv
./
2025-12-31_08-20-46/
2025-12-31_08-20-46/dns.csv
2025-12-31_08-20-46/domContentLoadedTime.csv
2025-12-31_08-20-46/first-contentful-paint.csv
2025-12-31_08-20-46/fullyLoaded.csv
2025-12-31_08-20-46/loadTime.csv
2025-12-31_08-20-46/ttfb.csv
sent 3,510,506 bytes received 215 bytes 7,021,442.00 bytes/sec
total size is 3,509,117 speedup is 1.001
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
5. 导出结果文件
将导出的 CSV 数据文件同步到 Nginx 对外服务目录:
bash
rsync -av --ignore-existing /docker/docker_data/volumes/docker_whisper/_data/sitespeed_csv/ \
/docker/sitespeed.io/sitespeed-result/sitespeed_csv/1
2
2
使用 `rsync` 的 `--ignore-existing` 参数可以避免覆盖已存在的文件,实现增量同步。
脚本工作原理
数据流说明
导出的指标说明
| 指标名称 | 说明 | 单位 |
|---|---|---|
| dns | DNS 解析时间 | ms |
| ttfb | Time to First Byte | ms |
| domContentLoadedTime | DOM 内容加载完成时间 | ms |
| first-contentful-paint | 首次内容绘制时间 | ms |
| loadTime | 最大内容绘制时间 | ms |
| fullyLoaded | 完全加载时间 | ms |